from typing import Dict, Any, Optional
import aiohttp
import asyncio
from urllib.parse import urljoin
import logging

class NetworkClient:
    """Network communication client for distributed system.
    
    This class provides asynchronous network communication capabilities,
    including HTTP requests and data transfer functionality.
    """
    
    def __init__(self, base_url: str, timeout: int = 30):
        """Initialize the network client.
        
        Args:
            base_url: Base URL for API endpoints
            timeout: Request timeout in seconds
        """
        self.base_url = base_url
        self.timeout = timeout
        self.session: Optional[aiohttp.ClientSession] = None
        self.logger = logging.getLogger("NetworkClient")
    
    async def start(self) -> None:
        """Start the network client."""
        if self.session is None:
            self.session = aiohttp.ClientSession(
                timeout=aiohttp.ClientTimeout(total=self.timeout)
            )
    
    async def stop(self) -> None:
        """Stop the network client."""
        if self.session is not None:
            await self.session.close()
            self.session = None
    
    async def get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """Send GET request.
        
        Args:
            endpoint: API endpoint
            params: Query parameters
            
        Returns:
            Dict[str, Any]: Response data
        """
        url = urljoin(self.base_url, endpoint)
        try:
            async with self.session.get(url, params=params) as response:
                response.raise_for_status()
                return await response.json()
        except aiohttp.ClientError as e:
            self.logger.error(f"GET request failed: {str(e)}")
            raise
    
    async def post(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
        """Send POST request.
        
        Args:
            endpoint: API endpoint
            data: Request data
            
        Returns:
            Dict[str, Any]: Response data
        """
        url = urljoin(self.base_url, endpoint)
        try:
            async with self.session.post(url, json=data) as response:
                response.raise_for_status()
                return await response.json()
        except aiohttp.ClientError as e:
            self.logger.error(f"POST request failed: {str(e)}")
            raise
    
    async def download_data(self, endpoint: str, save_path: str) -> bool:
        """Download data from remote endpoint.
        
        Args:
            endpoint: Data endpoint URL
            save_path: Path to save downloaded data
            
        Returns:
            bool: True if download successful
        """
        url = urljoin(self.base_url, endpoint)
        try:
            async with self.session.get(url) as response:
                response.raise_for_status()
                with open(save_path, 'wb') as f:
                    while True:
                        chunk = await response.content.read(8192)
                        if not chunk:
                            break
                        f.write(chunk)
            return True
        except (aiohttp.ClientError, IOError) as e:
            self.logger.error(f"Data download failed: {str(e)}")
            return False
    
    async def upload_data(self, endpoint: str, file_path: str) -> bool:
        """Upload data to remote endpoint.
        
        Args:
            endpoint: Upload endpoint URL
            file_path: Path to file for upload
            
        Returns:
            bool: True if upload successful
        """
        url = urljoin(self.base_url, endpoint)
        try:
            with open(file_path, 'rb') as f:
                data = aiohttp.FormData()
                data.add_field('file',
                              f,
                              filename=file_path.split('/')[-1])
                
                async with self.session.post(url, data=data) as response:
                    response.raise_for_status()
                    return True
        except (aiohttp.ClientError, IOError) as e:
            self.logger.error(f"Data upload failed: {str(e)}")
            return False